# -*- coding: utf-8 -*-
"""
单据工厂类 (BillFactory)

功能：
1. 根据配置动态创建表单控件
2. 管理主从表单据的界面布局
3. 处理数据模型与界面控件的映射关系
"""

from share.toolFunction import clear_layout_contents
from utils.billBase import BillBase
from typing import Dict, Optional, Any, List, Tuple
from PySide6.QtWidgets import (QDataWidgetMapper, QWidget, QMessageBox,
                               QGridLayout, QPlainTextEdit)
from mesParts.lineEdit.f7LineEdits import F7NameEdit, F7NumberEdit, F7SimpleFormEdt
from mesParts.lineEdit.lineEdits import LineEditStr, LineEditInt, LineEditDecimal
from mesParts.lineEdit.plainTextEdits import PlainTextEdit
from mesParts.label.labels import Label3
from mesParts.comboBox.comboBoxs import Combox
from mesParts.checkBox.checkBoxs import CheckBox
from mesParts.dateTime.dateTimes import DateEdit, DateTimeEdit, TimeEdit
from PySide6.QtCore import Qt, QObject
from typing import List, NamedTuple, Dict, Any
import json
import logging

class FieldConfig(NamedTuple):
    field: str
    domain: str
    widget: str
    label: str
    row: int
    column: int
    row_span: int
    column_span: int
    top: int
    visible: int
    is_edit: bool
    place_holder_text: str
    default_value: str
    must_input: int
    align: str

class BillFactory(BillBase):
    """单据处理工厂类，负责创建和管理单据界面元素及数据映射"""

    def __init__(self, parent = None) -> None:
        """
        初始化单据工厂

        Args:
            ms_dict: 主从表配置字典，包含：
                - 'msFNumber': 模块编码
                - 'msClass': 单据类型(表头表体/单表头/单表体)
                - 'msSaveType': 单据保存类型(-1-新增，1-维护旧记录)
                - 'FPID': 单据的父ID值(表头表的FID值)
        """
        super().__init__(parent)
        # 获取logger
        self.logger = logging.getLogger(__name__)

        self.page_browse_label_title = ''       # 浏览页面的标题
        self.page_bill_label_title = ''         # 单据页面的标题
        self.page_form_label_title = ''         # 表单页面的标题
        self.page_grid_label_title = ''         # 表格页面的标题

        self.browse_action_list = []    # 浏览界面工具栏命令列表
        self.browse_menu_list = []      # 浏览界面菜单项列表
        self.edit_action_list = []      # 编辑界面工具栏命令列表
        self.edit_menu_list = []        # 编辑界面菜单项列表

    # region    工厂初始化
    def create_factory(self, ms_dict: Optional[Dict] = None, parent: Optional[QWidget] = None):
        super().create_factory(ms_dict)
        if ms_dict:
            self._init_ui_components()

    def _init_ui_components(self) -> None:
        """初始化UI相关组件"""
        self.mapper = QDataWidgetMapper()
        self.h_obj_list: List[Optional[QWidget]] = []  # 存储字段控件对象
        self.h_lbl_list: List[Optional[QWidget]] = []  # 存储标签控件对象

        self._init_page_titles()    # 四类页面的标题初始化
        self._init_menu_action()

    def _init_page_titles(self) -> None:
        """
        使用json.loads()安全初始化四种页面标题
        参数要求：
            self.m_bill_table_name 应为合法JSON字符串，格式示例：
            '{"pageBrowse":"浏览页", "pageBill":"单据页", "pageForm":"表单页", "pageGrid":"表格页"}'

        属性设置：
            page_browse_label_title: 浏览页面标题(str)
            page_bill_label_title:   单据页面标题(str)
            page_form_label_title:   表单页面标题(str)
            page_grid_label_title:   表格页面标题(str)
        """
        # 初始化默认标题
        default_titles = {
            'pageBrowse': '',
            'pageBill': '',
            'pageForm': '',
            'pageGrid': ''
        }

        # 安全解析JSON配置
        try:
            if isinstance(self.m_bill_table_name, str):
                # 类型安全的JSON解析
                config_dict: Dict[str, Any] = json.loads(self.m_bill_table_name)
                if not isinstance(config_dict, dict):
                    raise ValueError("配置必须是字典格式")
            else:
                config_dict = {}
        except (json.JSONDecodeError, ValueError) as e:
            self.logger.info(f"[WARNING] 标题配置解析失败，使用默认值: {str(e)}")
            config_dict = {}
        except Exception as e:
            self.logger.info(f"[ERROR] 意外的配置解析错误: {str(e)}")
            config_dict = {}

        # 设置实例属性（合并配置和默认值）
        self.page_browse_label_title = config_dict.get('pageBrowse', default_titles['pageBrowse'])
        self.page_bill_label_title = config_dict.get('pageBill', default_titles['pageBill'])
        self.page_form_label_title = config_dict.get('pageForm', default_titles['pageForm'])
        self.page_grid_label_title = config_dict.get('pageGrid', default_titles['pageGrid'])

    def _init_menu_action(self) -> None:
        """
        初始化浏览/编辑页面的菜单和工具栏配置（直接设置类属性版）

        功能说明：
            1. 安全解析JSON配置初始化操作列表
            2. 直接设置类实例属性
            3. 自动处理字符串分割和空值情况

        参数要求：
            self.m_menus_actions 应为合法JSON字符串，格式示例：
            {
                "browseAction": "new,save,print",
                "editAction": "copy,paste",
                "browseMenu": "file,edit",
                "editMenu": "format,tools"
            }
        """
        # 1. 定义配置键映射 (外部键名 -> 类属性名)
        KEY_MAPPING = {
            'browseAction': 'browse_action_list',
            'editAction': 'edit_action_list',
            'browseMenu': 'browse_menu_list',
            'editMenu': 'edit_menu_list'
        }

        # 2. 初始化默认空列表
        for attr_name in KEY_MAPPING.values():
            setattr(self, attr_name, [])

        # 3. 安全解析JSON
        if not isinstance(self.m_menus_actions, str):
            return

        try:
            config_dict = json.loads(self.m_menus_actions)
            if not isinstance(config_dict, dict):
                raise ValueError("配置必须是字典格式")
        except (json.JSONDecodeError, ValueError) as e:
            self.logger.info(f"[WARNING] 配置解析失败: {str(e)}")
            return
        except Exception as e:
            print(f"[ERROR] 意外的配置错误: {str(e)}")
            return

        # 4. 处理并设置属性
        for config_key, attr_name in KEY_MAPPING.items():
            raw_value = config_dict.get(config_key, '')

            # 安全处理字符串分割
            if isinstance(raw_value, str):
                clean_list = [
                    item.strip()
                    for item in raw_value.split(',')
                    if item.strip()
                ]
            else:
                clean_list = []

            setattr(self, attr_name, clean_list)

    # endregion


    # region 创建Form
    def create_form(self, grid_top: QGridLayout, grid_foot: QGridLayout) -> None:
        """
        创建表单界面

        Args:
            grid_top: 页头布局
            grid_foot: 页脚布局
        """
        self._setup_layout_spacing(grid_top, grid_foot)
        self._clear_control_references()
        self._create_form_controls(grid_top, grid_foot)

    def _setup_layout_spacing(self, grid_top: QGridLayout, grid_foot: QGridLayout) -> None:
        """
        设置页头和页脚的布局间距和拉伸比例

        Args:
            grid_top: 页头网格布局
            grid_foot: 页脚网格布局
        """
        clear_layout_contents(grid_top)
        clear_layout_contents(grid_foot)

        # 设置页头布局
        # for i in range(0, self.m_head_columns):
        #     if i % 2 == 0:
        #         grid_top.setColumnStretch(i, 1)
        # grid_top.setHorizontalSpacing(self.m_head_spacer_height)

        grid_top.setHorizontalSpacing(3)

        # 设置页脚布局
        for i in range(0, self.m_foot_columns):
            grid_foot.setColumnStretch(i, 1)
        # grid_foot.setHorizontalSpacing(self.m_foot_spacer_height)

        grid_foot.setHorizontalSpacing(3)

    def _clear_control_references(self) -> None:
        """清空控件引用列表"""
        self.h_obj_list = []
        self.h_lbl_list = []

    # 创建控件的控制程序
    def _create_form_controls(self, grid_top: QGridLayout, grid_foot: QGridLayout) -> None:
        """根据配置创建表单控件"""

        for config in self._get_field_configs():

            if config.visible == 1:

                lbl, obj = self._create_control(config)

                if obj is not None:
                    self._position_control(lbl, obj, config, grid_top, grid_foot)
                    self._set_control_alignment(obj, config.align)
                    self._store_control_references(lbl, obj)
            else:
                self.h_lbl_list.append(None)
                self.h_obj_list.append(None)

    def _get_field_configs(self) -> List[FieldConfig]:
        """获取字段配置的迭代器"""
        return [
            FieldConfig(*args) for args in zip(
                self.h_field_list,
                self.h_domain_list,
                self.h_widget_list,
                self.h_label_name_list,
                self.h_row_list,
                self.h_column_list,
                self.h_row_span_list,
                self.h_column_span_list,
                self.h_top_foot_list,
                self.h_visible_list,
                self.h_edit_list,
                self.h_place_holder_text_list,
                self.h_default_val_list,
                self.h_must_input_list,
                self.h_align_list
            )
        ]

    def _create_control(self, config: FieldConfig) -> Tuple[Optional[QWidget], Optional[QWidget]]:
        """
        根据配置创建控件，确保始终返回 (label, widget) 元组
        Args:
            config: 字段配置对象，必须包含 widget 属性
        Returns:
            Tuple[可选标签控件, 必填字段控件] （标签可能为None）
        """

        control_creators = {
            'Label3': self._create_label_control,
            'LineEditStr': self._create_line_edit_str,
            'LineEditInt': self._create_line_edit_int,
            'LineEditDecimal': self._create_line_edit_decimal,
            'F7NameEdit': self._create_f7_name_edit,
            'F7NumberEdit': self._create_f7_number_edit,
            'F7SimpleFormEdt': self._create_f7_simple_edit,
            'Combox': self._create_combobox,
            'DateEdit': self._create_date_edit,
            'CheckBox': self._create_checkbox,
            'TimeEdit': self._create_time_edit,
            'DateTimeEdit': self._create_datetime_edit,
            'PlainTextEdit': self._create_plain_text_edit
        }

        # 获取创建方法，不存在时返回 (None, None)

        creator = control_creators.get(config.widget)
        if creator is None:
            print(f"Warning: Unknown widget type '{config.widget}', returning (None, None)")
            return (None, None)

        # 调用创建方法并验证返回值
        try:
            result = creator(config)
            if not isinstance(result, tuple) or len(result) != 2:
                raise ValueError(f"Creator for '{config.widget}' must return (label, widget), got {result}")

            return result
        except Exception as e:
            print(f"Error creating control '{config.widget}': {str(e)}")
            return (None, None)
    # endregion


    # region 具体控件创建
    # 以下是创建具体控件
    def _create_label_control(self, config: Any) -> Tuple[Label3, Label3]:
        """创建标签控件"""
        lbl = Label3(self)
        lbl.setObjectName(config.field)
        obj = Label3(self)
        obj.setObjectName(config.field)
        obj.setEnabled(config.is_edit and self.__m_class != -1)
        return lbl, obj

    def _create_line_edit_str(self, config: Any) -> Tuple[Label3, LineEditStr]:
        """创建字符串输入框"""
        lbl = Label3(config.label)
        lbl.setObjectName(config.field)
        self._set_must_input_flag(lbl, config.must_input)
        obj = LineEditStr(self)
        self._setup_control_basic_properties(obj, config)
        self._set_edit_flag(obj, config.is_edit)
        obj.textChangedSignal.connect(self._handle_input_widget_data_change)

        return lbl, obj

    def _create_line_edit_int(self, config: Any) -> Tuple[Label3, LineEditInt]:
        """创建整数输入框"""
        lbl = Label3(config.label)
        lbl.setObjectName(config.field)
        self._set_must_input_flag(lbl, config.must_input)
        obj = LineEditInt(self)
        self._setup_control_basic_properties(obj, config)
        self._set_edit_flag(obj, config.is_edit)
        return lbl, obj

    def _create_line_edit_decimal(self, config: Any) -> Tuple[Label3, LineEditDecimal]:
        """创建浮点数输入框"""
        lbl = Label3(config.label)
        lbl.setObjectName(config.field)
        self._set_must_input_flag(lbl, config.must_input)
        obj = LineEditDecimal(self)
        self._setup_control_basic_properties(obj, config)
        self._set_edit_flag(obj, config.is_edit)
        return lbl, obj

    def _create_f7_name_edit(self, config: Any) -> Tuple[Label3, F7NameEdit]:
        """创建f7名称输入框"""
        lbl = Label3(config.label)
        lbl.setObjectName(config.field)
        self._set_must_input_flag(lbl, config.must_input)
        p_dict = {'work': self}
        obj = F7NameEdit(**p_dict)
        obj.label = lbl
        self._setup_control_basic_properties(obj, config)
        obj.textChangedSignal.connect(self._handle_input_widget_data_change)
        return lbl, obj

    def _create_f7_number_edit(self, config: Any) -> Tuple[Label3, F7NumberEdit]:
        """创建f7代码输入框"""
        lbl = Label3(config.label)
        lbl.setObjectName(config.field)
        self._set_must_input_flag(lbl, config.must_input)
        p_dict = {'work': self}
        obj = F7NumberEdit(**p_dict)
        obj.label = lbl
        self._setup_control_basic_properties(obj, config)
        obj.textChangedSignal.connect(self._handle_input_widget_data_change)
        return lbl, obj

    def _create_f7_simple_edit(self, config: Any) -> Tuple[Label3, F7NumberEdit]:
        """创建f7代码 F7SimplePanel输入框"""
        lbl = Label3(config.label)
        lbl.setObjectName(config.field)
        self._set_must_input_flag(lbl, config.must_input)
        p_dict = {'work': self}
        obj = F7SimpleFormEdt(**p_dict)
        obj.label = lbl
        self._setup_control_basic_properties(obj, config)
        obj.textChangedSignal.connect(self._handle_input_widget_data_change)
        return lbl, obj

    def _create_combobox(self, config: Any) -> Tuple[Label3, Combox]:
        """创建combobox"""
        lbl = Label3(config.label)
        lbl.setObjectName(config.field)
        self._set_must_input_flag(lbl, config.must_input)
        val_list = self.get_domain_list(config.domain)
        obj = Combox(val_list, config.default_value)
        self._setup_control_basic_properties(obj, config)
        return lbl, obj

    def _create_checkbox(self, config: Any) -> Tuple[Label3, CheckBox]:
        """创建checkbox"""
        lbl = Label3(config.label)
        obj = CheckBox(self)
        self._set_check_box_enabled(obj, config.is_edit)
        return lbl, obj

    def _create_date_edit(self, config: Any) -> Tuple[Label3, DateEdit]:
        """创建日期控件"""
        lbl = Label3(config.label)
        obj = DateEdit()
        obj.setCalendarPopup(True)
        self._setup_control_basic_properties(obj, config)
        self._set_edit_flag(obj, config.is_edit)
        return lbl, obj

    def _create_datetime_edit(self, config: Any) -> Tuple[Label3, DateTimeEdit]:
        """创建日期时间控件"""
        lbl = Label3(config.label)
        obj = DateTimeEdit()
        obj.setCalendarPopup(True)
        self._setup_control_basic_properties(obj, config)
        self._set_edit_flag(obj, config.is_edit)
        return lbl, obj

    def _create_time_edit(self, config: Any) -> Tuple[Label3, TimeEdit]:
        """创建时间控件"""
        lbl = Label3(config.label)
        obj = TimeEdit()
        self._setup_control_basic_properties(obj, config)
        return lbl, obj

    def _create_plain_text_edit(self, config: Any) -> Tuple[Label3, PlainTextEdit]:
        """创建plaintext控件"""
        lbl = Label3(config.label)
        obj = PlainTextEdit()
        self._setup_control_basic_properties(obj, config)
        return lbl, obj

    # endregion


    # region 控件位置设置，对齐，必录 控件暂存， 其他属性设置
    def _position_control(self,
                          lbl: Optional[QWidget],
                          obj: QWidget,
                          config: Any,
                          grid_top: QGridLayout,
                          grid_foot: QGridLayout) -> None:
        """
        设置控件在布局中的位置

        Args:
            lbl: 标签控件
            obj: 字段控件
            config: 字段配置
            grid_top: 页头布局
            grid_foot: 页脚布局
        """
        # 设置控件大小策略
        if lbl is not None:
            lbl.setMinimumWidth(80)
            lbl.setMaximumWidth(180)
        obj.setMinimumWidth(100)
        obj.setMaximumWidth(2000)

        # 添加到对应布局
        target_grid = grid_top if config.top == 0 else grid_foot
        if lbl is not None:
            target_grid.addWidget(lbl, config.row, config.column,
                                  config.row_span, config.column_span)
        target_grid.addWidget(obj, config.row, config.column + 1,
                              config.row_span, config.column_span)

        # 设置列拉伸比例
        target_grid.setColumnStretch(config.column, 1)
        target_grid.setColumnStretch(config.column + 1, 2)

    @staticmethod
    def _set_control_alignment(obj: QWidget, align: str) -> None:
        """设置控件对齐方式"""
        if not hasattr(obj, 'setAlignment'):
            return

        alignment_map = {
            'Qt.AlignmentFlag.AlignLeft': Qt.AlignmentFlag.AlignLeft,
            'Qt.AlignmentFlag.AlignRight': Qt.AlignmentFlag.AlignRight,
            'Qt.AlignmentFlag.AlignCenter': Qt.AlignmentFlag.AlignCenter
        }

        if align in alignment_map:
            obj.setAlignment(alignment_map[align])

    def _store_control_references(self, lbl: Optional[QWidget], obj: QWidget) -> None:
        """保存控件引用"""

        if lbl is not None:
            self.h_lbl_list.append(lbl)
        if obj is not None:
            self.h_obj_list.append(obj)

    @staticmethod
    def _set_must_input_flag(lbl: Label3, must_input: int) -> None:
        """设置必填标志"""
        if must_input == 1:
            lbl.setText(f"🔵{lbl.text()}")

    @staticmethod
    def _set_edit_flag(obj: Any, is_edit: int) -> None:
        """
        设置控件是否可编辑
        :return:
        """

        if is_edit:
            obj.setReadOnly(False)
        else:
            obj.setReadOnly(True)

    @staticmethod
    def _set_check_box_enabled(obj: Any, is_edit: int) -> None:
        """设置checkBox可否点击"""
        if is_edit:
            obj.setEnabled(True)
        else:
            obj.setEnabled(False)
            obj.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents, True)
            obj.setFocusPolicy(Qt.FocusPolicy.NoFocus)

    @staticmethod
    def _setup_control_basic_properties(obj: QObject, config: Any) -> None:
        """设置控件基本属性"""
        obj.setObjectName(config.field)
        # obj.setEnabled(config.is_edit and self.__m_class != -1)
        if hasattr(obj, 'setPlaceholderText') and config.place_holder_text:
            obj.setPlaceholderText(config.place_holder_text)

        # 设置对齐方式
        if hasattr(obj, 'setAlignment') and config.align:
            if config.align == 'Qt.AlignRight' or config.align.lower() == 'right':
                obj.setAlignment(Qt.AlignmentFlag.AlignRight)
            elif config.align == 'Qt.AlignLeft' or config.align.lower() == 'left':
                obj.setAlignment(Qt.AlignmentFlag.AlignLeft)
            elif config.align == 'Qt.AlignCenter' or config.align.lower() == 'center':
                obj.setAlignment(Qt.AlignmentFlag.AlignCenter)
            else:
                obj.setAlignment(Qt.AlignmentFlag.AlignLeft)

    # endregion


    # region 处理输入控件的信号, 获取必录字段， 获取输入控件和label
    def _handle_input_widget_data_change(self, obj) -> None:
        """
        接受来自输入控件的数据变化的信号
        :param obj:
        :return:
        """
        field_name = obj.objectName().lower()
        label, obj = self._get_must_input_lbl_obj(field_name)
        if obj.text() != '':
            label.setMust(0)

    def _get_h_must_input_field(self) -> List[str]:
        """
        获取表头必录字段列表

        根据必录标志列表(h_must_input_list)和字段列表(h_field_list)，
        返回所有必须录入的字段名列表。

        Returns:
            List[str]: 必须录入的字段名称列表
        """
        return [
            field
            for field, is_must in zip(self.h_field_list, self.h_must_input_list)
            if is_must
        ]

    def _get_b_must_input_field(self) -> List[str]:
        """
        获取表头必录字段列表
        :return:
        """

        return [
            field
            for field, is_must in zip(self.b_field_list, self.b_must_input_list)
            if is_must
        ]

    def _get_must_input_lbl_obj(self, field_name: str) -> Tuple[Optional[Any], Optional[Any]]:
        if not field_name:
            return None, None

        field_lower = field_name.lower()

        def find_in(lst):
            for item in lst:
                if item not in [None, '']:
                    if item.objectName().lower() == field_lower:
                        return item
            return None

        return (find_in(self.h_lbl_list),
                find_in(self.h_obj_list))

    def _get_h_label(self, field_name: str) -> Any:
        """
        获取指定名称的label对象
        :param field_name:
        :return:
        """
        field_lower = field_name.lower()
        def find_in(lst):
            for item in lst:
                if item not in [None, '']:
                    if item.objectName().lower() == field_lower:
                        return item
            return None

        return find_in(self.h_lbl_list)

    # endregion


